package com.tyf.common.service;

import java.io.*;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.List;

import me.lightspeed7.mongofs.MongoFile;
import me.lightspeed7.mongofs.MongoFileCursor;
import me.lightspeed7.mongofs.MongoFileStore;
import me.lightspeed7.mongofs.MongoFileStoreConfig;
import me.lightspeed7.mongofs.MongoFileWriter;
import me.lightspeed7.mongofs.url.MongoFileUrl;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.bson.types.ObjectId;
import org.mongodb.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.stereotype.Component;

@Component
public class MongoFileService {

    private static Logger log = Logger.getLogger(MongoFileService.class);

    private final MongoDbFactory mongo;

    private MongoFileStore mgFileStore;

    @Autowired
    public MongoFileService(MongoDbFactory mongo) {
        this.mongo = mongo;
    }


    public MongoFileStore getMgFileStore() {
        try {
            if (mgFileStore == null) {
                MongoFileStoreConfig config = MongoFileStoreConfig.builder().build();// start builder
                mgFileStore = new MongoFileStore(mongo.getDb(), config);
            }
            return mgFileStore;
        } catch (Exception e) {
            log.error(e);
        }
        return null;
    }


    /**
     * 获取存储仓库
     *
     * @param bucket 集合 | 表
     * @return
     */
    public MongoFileStore getMgFileStore(String bucket) {
        try {
            MongoFileStoreConfig config = MongoFileStoreConfig.builder().bucket(bucket).build();// start builder
            mgFileStore = new MongoFileStore(mongo.getDb(), config);
            return mgFileStore;
        } catch (Exception e) {
            log.error(e);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param filePath  文件路径名称
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String filePath, String mediaType) {
        try {
            MongoFile mgFile = insertMongoFile(filePath, mediaType);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param bucket    集合名
     * @param filePath  文件路径名称
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String bucket, String filePath, String mediaType) {
        try {
            MongoFile mgFile = insertMongoFile(bucket, filePath, mediaType);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 适合临时文件存储
     *
     * @param filePath  文件路径
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @param expires   有效期
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String filePath, String mediaType, Date expires) {
        try {
            MongoFile mgFile = insertMongoFile(filePath, mediaType, expires);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }

    /**
     * 适合临时文件存储
     *
     * @param bucket    集合名
     * @param filePath  文件路径
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @param expires   有效期
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String bucket, String filePath, String mediaType, Date expires) {
        try {
            MongoFile mgFile = insertMongoFile(bucket, filePath, mediaType, expires);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param fileName  文件名称
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @param input     输入流
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String fileName, String mediaType, InputStream input) {
        try {
            MongoFile mgFile = insertMongoFile(fileName, mediaType, input);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param bucket    集合名
     * @param fileName  文件名称
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @param input     输入流
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String bucket, String fileName, String mediaType, InputStream input) {
        try {
            MongoFile mgFile = insertMongoFile(bucket, fileName, mediaType, input);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param fileName  文件名称
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @param input     输入流
     * @param expires   有效期
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String fileName, String mediaType, InputStream input, Date expires) {
        try {
            MongoFile mgFile = insertMongoFile(fileName, mediaType, input, expires);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param bucket    集合名
     * @param fileName  文件名称
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @param input     输入流
     * @param expires   有效期
     * @return URL (for example: 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public String insert(String bucket, String fileName, String mediaType, InputStream input, Date expires) {
        try {
            MongoFile mgFile = insertMongoFile(bucket, fileName, mediaType, input, expires);
            if (mgFile != null) {
                return mgFile.getURL().toString();
            }
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param filePath  文件路径 (for example : '/data/jpn/input.txt')
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @return MongoFile 文件内容
     */
    public MongoFile insertMongoFile(String filePath, String mediaType) {
        FileInputStream fis = null;
        try {
            long begin = System.currentTimeMillis();

            //is File
            File file = new File(filePath);
            if (!file.isFile()) {
                throw new Exception("not File,Path:" + filePath);
            }
            //获取仓库
            MongoFileStore store = getMgFileStore();

            fis = new FileInputStream(file);
            MongoFileWriter writer = store.createNew(file.getName(), mediaType);
            writer.write(fis);

            IOUtils.closeQuietly(fis);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile mgFile = writer.getMongoFile();
            return mgFile;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(fis);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param bucket    集合
     * @param filePath  filePath 文件路径 (for example : '/data/jpn/input.txt')
     * @param mediaType Media Type <br>{@link com.euond.common.mongodb.MediaType}
     * @return 文件内容
     */
    public MongoFile insertMongoFile(String bucket, String filePath, String mediaType) {
        FileInputStream fis = null;
        try {
            long begin = System.currentTimeMillis();

            //is File
            File file = new File(filePath);
            if (!file.isFile()) {
                throw new Exception("not File,Path:" + filePath);
            }
            //获取仓库
            MongoFileStore store = getMgFileStore(bucket);

            fis = new FileInputStream(file);
            MongoFileWriter writer = store.createNew(file.getName(), mediaType);
            writer.write(fis);

            IOUtils.closeQuietly(fis);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile mgFile = writer.getMongoFile();
            return mgFile;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(fis);
        }
        return null;
    }

    /**
     * 适合临时文件存储
     *
     * @param filePath  文件路径
     * @param mediaType 类型
     * @param expires   有效期
     * @return
     */
    public MongoFile insertMongoFile(String filePath, String mediaType, Date expires) {
        FileInputStream fis = null;
        try {
            long begin = System.currentTimeMillis();

            if (expires == null) {
                throw new IllegalArgumentException("Date Expires cannot be null");
            }
            //is File
            File file = new File(filePath);
            if (!file.isFile()) {
                throw new IllegalArgumentException("File cannot be null,Path:" + filePath);
            }
            MongoFileStore store = getMgFileStore();

            fis = new FileInputStream(file);
            MongoFileWriter writer = store.createNew(file.getName(), mediaType, expires, false);
            writer.write(fis);

            IOUtils.closeQuietly(fis);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile mgFile = writer.getMongoFile();
            return mgFile;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(fis);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param fileName  文件名
     * @param mediaType 类型
     * @param input     输入流
     * @return
     */
    public MongoFile insertMongoFile(String fileName, String mediaType, InputStream input) {
        try {
            long begin = System.currentTimeMillis();

            if (fileName == null || fileName.isEmpty()) {
                throw new IllegalArgumentException("File name cannot be null");
            }
            if (input == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }
            MongoFileStore store = getMgFileStore();

            MongoFileWriter writer = store.createNew(fileName, mediaType);
            writer.write(input);

            IOUtils.closeQuietly(input);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile file = writer.getMongoFile();
            return file;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(input);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param bucket    集合名
     * @param fileName  文件名
     * @param mediaType 类型
     * @param input     输入流
     * @return
     */
    public MongoFile insertMongoFile(String bucket, String fileName, String mediaType, InputStream input) {
        try {
            long begin = System.currentTimeMillis();

            if (fileName == null || fileName.isEmpty()) {
                throw new IllegalArgumentException("File name cannot be null");
            }
            if (input == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }
            MongoFileStore store = getMgFileStore(bucket);

            MongoFileWriter writer = store.createNew(fileName, mediaType);
            writer.write(input);

            IOUtils.closeQuietly(input);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile file = writer.getMongoFile();
            return file;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(input);
        }
        return null;
    }


    /**
     * 适合临时文件存储
     *
     * @param bucket    集合名
     * @param filePath  文件路径
     * @param mediaType 类型
     * @param expires   有效期
     * @return
     */
    public MongoFile insertMongoFile(String bucket, String filePath, String mediaType, Date expires) {
        FileInputStream fis = null;
        try {
            long begin = System.currentTimeMillis();

            if (expires == null) {
                throw new IllegalArgumentException("Date Expires cannot be null");
            }
            //is File
            File file = new File(filePath);
            if (!file.isFile()) {
                throw new IllegalArgumentException("File cannot be null,Path:" + filePath);
            }
            MongoFileStore store = getMgFileStore(bucket);

            fis = new FileInputStream(file);
            MongoFileWriter writer = store.createNew(file.getName(), mediaType, expires, false);
            writer.write(fis);

            IOUtils.closeQuietly(fis);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile mgFile = writer.getMongoFile();
            return mgFile;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(fis);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param fileName  文件名称
     * @param mediaType 类型
     * @param input     输入流
     * @param expires   有效期
     * @return
     */
    public MongoFile insertMongoFile(String fileName, String mediaType, InputStream input, Date expires) {
        try {
            long begin = System.currentTimeMillis();

            if (expires == null) {
                throw new IllegalArgumentException("Date Expires cannot be null");
            }
            if (fileName == null || fileName.isEmpty()) {
                throw new IllegalArgumentException("File name cannot be null");
            }
            if (input == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }
            MongoFileStore store = getMgFileStore();

            MongoFileWriter writer = store.createNew(fileName, mediaType, expires, false);
            writer.write(input);

            IOUtils.closeQuietly(input);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile file = writer.getMongoFile();
            return file;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(input);
        }
        return null;
    }

    /**
     * 新增文件
     *
     * @param bucket    集合名称
     * @param fileName  文件名
     * @param mediaType 类型
     * @param input     输入流
     * @param expires   有效期
     * @return
     */
    public MongoFile insertMongoFile(String bucket, String fileName, String mediaType, InputStream input, Date expires) {
        try {
            long begin = System.currentTimeMillis();

            if (expires == null) {
                throw new IllegalArgumentException("Date Expires cannot be null");
            }
            if (fileName == null || fileName.isEmpty()) {
                throw new IllegalArgumentException("File name cannot be null");
            }
            if (input == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }

            MongoFileStore store = getMgFileStore(bucket);

            MongoFileWriter writer = store.createNew(fileName, mediaType, expires, false);
            writer.write(input);

            IOUtils.closeQuietly(input);

            long end = System.currentTimeMillis();
            log.debug("Insert Complete! Cost " + (end - begin) + "/ms");

            MongoFile file = writer.getMongoFile();
            return file;
        } catch (Exception e) {
            log.error(e.getMessage());
            IOUtils.closeQuietly(input);
        }
        return null;
    }

    /**
     * 根据文件名查找，可能会返回多个文件
     *
     * @param fileName 文件名称
     * @return
     */
    public List<MongoFile> find(String fileName) {
        try {
            MongoFileStore store = getMgFileStore(fileName);
            List<MongoFile> mgFiles = store.find(fileName).toList();

            return mgFiles;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 根据文件名查找，可能会返回多个文件
     *
     * @param bucket   集合
     * @param fileName 文件名称
     * @return
     */
    public List<MongoFile> find(String bucket, String fileName) {
        try {
            MongoFileStore store = getMgFileStore(bucket);
            List<MongoFile> mgFiles = store.find(fileName).toList();

            return mgFiles;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }


    public List<MongoFile> find(final Document query) {
        MongoFileStore store = getMgFileStore();
        MongoFileCursor cursor = store.find(query);
        List<MongoFile> mgFiles = cursor.toList();
        return mgFiles;
    }

    public List<MongoFile> find(String bucket, final Document query) {
        MongoFileStore store = getMgFileStore(bucket);
        MongoFileCursor cursor = store.find(query);
        List<MongoFile> mgFiles = cursor.toList();
        return mgFiles;
    }


    public List<MongoFile> find(final Document query, final Document sort) {
        MongoFileStore store = getMgFileStore();
        MongoFileCursor cursor = store.find(query, sort);
        List<MongoFile> mgFiles = cursor.toList();
        return mgFiles;
    }

    public List<MongoFile> find(String bucket, final Document query, final Document sort) {
        MongoFileStore store = getMgFileStore(bucket);
        MongoFileCursor cursor = store.find(query, sort);
        List<MongoFile> mgFiles = cursor.toList();
        return mgFiles;
    }

    /**
     * 根据主键ObjectId查询
     *
     * @param id 主键
     * @return
     */
    public MongoFile findOne(ObjectId id) {
        try {
            MongoFileStore store = getMgFileStore();
            MongoFile mgFile = store.findOne(id);
            return mgFile;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    public MongoFile findOne(String bucket, ObjectId id) {
        try {
            MongoFileStore store = getMgFileStore(bucket);
            MongoFile mgFile = store.findOne(id);
            return mgFile;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 查找
     *
     * @param url 唯一标识符
     *            <br>mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain
     * @return
     */
    public MongoFile findOne(String url) {
        try {
            MongoFileStore store = getMgFileStore();
            MongoFileUrl mfUrl = MongoFileUrl.construct(url);
            MongoFile mgFile = store.findOne(mfUrl);
            return mgFile;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 查找
     *
     * @param bucket 集合
     * @param url    唯一标识符
     *               <br>mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain
     * @return
     */
    public MongoFile findOne(String bucket, String url) {
        try {
            MongoFileStore store = getMgFileStore(bucket);
            MongoFileUrl mfUrl = MongoFileUrl.construct(url);
            MongoFile mgFile = store.findOne(mfUrl);
            return mgFile;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
        return null;
    }

    /**
     * 查找
     *
     * @param url 唯一标识符
     *            <br>(for example : 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     * @return
     */
    public byte[] findOneToByte(String url) {
        ByteArrayOutputStream out = null;
        try {

            out = new ByteArrayOutputStream();
            MongoFile mgFile = findOne(url);
            mgFile.readInto(out, true);
            byte[] bytes = out.toByteArray();

            return bytes;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        } finally {
            if (out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    log.error(e);
                }
            }
        }
        return null;
    }

    public byte[] findOneToByte(String bucket, String url) {
        ByteArrayOutputStream out = null;
        try {
            out = new ByteArrayOutputStream();
            MongoFile mgFile = findOne(bucket, url);
            mgFile.readInto(out, true);
            byte[] bytes = out.toByteArray();

            return bytes;
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }finally {
            if (out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    log.error(e);
                }
            }
        }
        return null;
    }

    /**
     * 删除文件
     *
     * @param mgUrl
     */
    public void delete(MongoFileUrl mgUrl) {
        try {
            MongoFileStore store = getMgFileStore();
            store.remove(mgUrl);
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
    }

    public void delete(String bucket, MongoFileUrl mgUrl) {
        try {
            MongoFileStore store = getMgFileStore(bucket);
            store.remove(mgUrl);
        } catch (Exception e) {
            // TODO: handle exception
            log.error(e);
        }
    }

    /**
     * 删除文件
     *
     * @param url 唯一标识符
     *            <br>(for example : 'mongofile:gz:mdb.txt?578c7a2636851e0e30115418#text/plain')
     */
    public void delete(String url) {
        try {
            MongoFileStore store = getMgFileStore();
            MongoFileUrl mgUrl = MongoFileUrl.construct(url);
            store.remove(mgUrl);
            log.debug("Delete Success !");
        } catch (MalformedURLException e) {
            // TODO: handle exception
            log.error(e);
        }
    }

    /**
     * 删除文件
     *
     * @param bucket 集合名
     * @param url    文件URI
     */
    public void delete(String bucket, String url) {
        try {
            MongoFileStore store = getMgFileStore(bucket);
            MongoFileUrl mgUrl = MongoFileUrl.construct(url);
            store.remove(mgUrl);
            log.debug("Delete Success !");
        } catch (MalformedURLException e) {
            // TODO: handle exception
            log.error(e);
        }
    }


    /**
     * 设置文件有效时间
     *
     * @param url the url to Mongo File
     * @param when - the date to expire the file by
     */
    public void expireFile(final String url, final Date when) {
        try {
            MongoFile file = findOne(url);
            if (file != null) {
                getMgFileStore().expireFile(file, when);
            }
        } catch (MalformedURLException e) {
            // TODO: handle exception
            log.error(e);
        }
    }

    /**
     * 设置文件有效期
     *
     * @param bucket 集合名称
     * @param url    文件url
     * @param when   有效期
     */
    public void expireFile(String bucket, final String url, final Date when) {
        try {
            MongoFile file = findOne(url);
            if (file != null) {
                getMgFileStore(bucket).expireFile(file, when);
            }
        } catch (MalformedURLException e) {
            // TODO: handle exception
            log.error(e);
        }
    }

    public MongoDbFactory getMongo() {
        return mongo;
    }

    public void setMgFileStore(MongoFileStore mgFileStore) {
        this.mgFileStore = mgFileStore;
    }
}
